const Module = require('module');
const Graph = require('./core/Graph');
const path = require('path');
const util = require('./core/util');

const nodeModuleRegex = /^(?:.*[\\/])?node_modules(?:[\\/].*)?$/;

const entry = module.parent.filename;
const graph = new Graph(entry);

const originalModuleLoad = Module._load;
Module._load = function(request, parent, isMain) {
  const filename = Module._resolveFilename(request, parent, isMain);
  const isNodeModule = nodeModuleRegex.test(filename);
  const isProjectFile = path.extname(filename) === '.js' && !isNodeModule;
  let from;
  if (isProjectFile) {
    from = graph.addEdge(parent.filename, filename).from;
  }
  const exports = originalModuleLoad(request, parent, isMain);
  if (from && util.isEmptyExport(exports)) {
    from.addBadRequire(filename, exports);
    return new Proxy(exports, {
      get(target, p, receiver) {
        const value = target[p];
        from.addEarlyAccess(filename, p, value);
        return value;
      },
    });
  }
  return exports;
};

setImmediate(() => {
  Module._load = originalModuleLoad;
});

let circulations;

module.exports = {
  /**
   * 打印循环引用信息的console
   */
  print() {
    setImmediate(() => {
      if (!circulations) {
        circulations = graph.findCirculation();
      }
      if (circulations.length) {
        console.error(
            util.red(`发现【${circulations.length}】条循环引用: (输出链路中【倒数第二】个文件对【最后】一个文件的require操作可能没有获取预期值)`));
        console.error(`- ${util.red(util.underline(`可能是require得到了空对象，而非目标文件定义的exports内容`))}`)
        console.error(`- ${util.red(util.underline(`可能是对require得到的对象的属性的访问结果不是预期值`))}`)
        console.error()
        circulations.forEach(item => {
          const c = item.badRequireChain
          const reason = item.reason
          const {
            prefix,
            paths,
          } = util.extractCommonPrefix(c);
          if (prefix) {
            console.error(`${util.red('前缀：')}${prefix}`);
            console.error(`${paths.join('  ->  ')}`);
          } else {
            console.error(c.join('  ->  '));
          }
          console.error(util.red('问题：'));
          console.error(`${reason}`)
          console.error();
        });
      } else {
        console.log('未发现有害的循环引用');
        console.log('检测循环引用的过程可能会修改require函数的返回值，如果你的代码依赖某些判断，可能引起bug');
        console.log('删掉检测循环引用的代码，重新启动服务即可');
      }
    });
  },
  /**
   * 查询指定文件依赖哪些文件，也就是require了哪些文件
   * @param {string} filename 要查询的文件完整路径
   * @return {string[]}
   *
   * @example
   * console.log(findDependencies(__filename))
   */
  findDependencies(filename) {
    const node = graph.findNode(filename);
    if (!node) {
      throw new Error(`"${filename}" 未找到该节点`);
    }
    return node.next.map(item => item.filename);
  },
};
